using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.IO;

// LAYOUT:
// signature    4b (uint)    0
// version      4b (uint)    4
// capacity     4b (int)     8
// reserved   400b (...)    12
// <entries>       (...)   412

// ENTRY LAYOUT:
// created year     4b (int)     0
// created month    1b (byte)    4
// created day      1b (byte)    5
// created hour     1b (byte)    6
// created minute   1b (byte)    7
// created second   1b (byte)    8
// title ID         4b (int)     9
// text  ID         4b (int)    13

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class NoteFile : UnorderedListFile<NoteFile.NoteEntry>
    {
        #region Types

        public /*internal*/ struct NoteEntry
        {
            public int TitleID;
            public int TextID;
            public DateTime Created;
        }

        #endregion // Types

        #region Constants

        private const uint SIGNATURE = 0x31313131;
        private const uint VERSION = 1;
        private const int OFFSET_CAPACITY = 8;
        private const int OFFSET_DATA = 412;
        private const int OFFSET_ENTRY_CREATED_YEAR = 0;
        private const int OFFSET_ENTRY_TITLE_ID = 9;
        private const int OFFSET_ENTRY_TEXT_ID = 13;
        private const int ENTRY_BYTE_SIZE = 17;

        #endregion // Constants

        #region Create/Open Methods

        public static NoteFile Create(string path, bool overwrite)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));

            if (File.Exists(path) && !overwrite)
            {
                throw new IOException(String.Format("File \"{0}\" is already exists.", path));
            }

            FileStream stream = null;
            BinaryWriter writer = null;
            BinaryReader reader = null;

            try
            {
                stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                writer = new BinaryWriter(stream, Encoding.UTF8);
                reader = new BinaryReader(stream, Encoding.UTF8);

                writer.Write(SIGNATURE);
                writer.Write(VERSION);
                writer.Write((int)0);
                writer.WriteBytes((byte)0, 400);

                writer.Flush();
                writer.Seek(0, SeekOrigin.Begin);

                return new NoteFile(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                throw;
            }
        }

        public static NoteFile Open(string path)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));

            FileStream stream = null;
            BinaryWriter writer = null;
            BinaryReader reader = null;

            try
            {
                stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                writer = new BinaryWriter(stream, Encoding.UTF8);
                reader = new BinaryReader(stream, Encoding.UTF8);

                return new NoteFile(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                throw;
            }
        }

        #endregion // Create/Open Methods

        #region Constructor

        private NoteFile(FileStream stream, BinaryReader reader, BinaryWriter writer):
            base(stream, reader, writer, SIGNATURE, VERSION)
        {
        }

        #endregion // Constructor

        #region UnorderedListFile<NoteEntry> Members

        protected override long CapacityOffset
        {
            get
            {
                return OFFSET_CAPACITY;
            }
        }

        protected override long DataOffset
        {
            get
            {
                return OFFSET_DATA;
            }
        }

        protected override int EntryByteSize
        {
            get
            {
                return ENTRY_BYTE_SIZE;
            }
        }

        protected override NoteEntry ReadData(long offset, SeekOrigin origin)
        {
            // TOFIX: should the default params be overwriten here?

            if (offset >= 0)
            {
                _reader.BaseStream.Seek(offset, origin);
            }

            var entry = new NoteEntry();

            entry.Created = _reader.ReadDateTime();
            entry.TitleID = _reader.ReadInt32();
            entry.TextID = _reader.ReadInt32();

            return entry;
        }

        protected override void WriteData(NoteEntry entry, long offset, SeekOrigin origin)
        {
            // TOFIX: should the default params be overwriten here?

            if (offset >= 0)
            {
                _writer.Seek((int)offset, origin);
            }

            _writer.Write(entry.Created);
            _writer.Write(entry.TitleID);
            _writer.Write(entry.TextID);

            _writer.Flush();
        }

        #endregion // UnorderedListFile<NoteEntry> Members

        #region Public Methods

        public int AddEntry(int titleID, int textID, DateTime created)
        {
            return AddEntry(new NoteEntry() { Created = created, TitleID = titleID, TextID = textID });
        }

        public int GetEntryTitleID(int id)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            return _reader.ReadInt32(GetEntryDataOffset(id) + OFFSET_ENTRY_TITLE_ID);
        }

        public void SetEntryTitleID(int id, int titleID)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            _writer.Write(titleID, (int)GetEntryDataOffset(id) + OFFSET_ENTRY_TITLE_ID);
        }

        public int GetEntryTextID(int id)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            return _reader.ReadInt32(GetEntryDataOffset(id) + OFFSET_ENTRY_TEXT_ID);
        }

        public void SetEntryTextID(int id, int textID)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            _writer.Write(textID, (int)GetEntryDataOffset(id) + OFFSET_ENTRY_TEXT_ID);
        }

        public DateTime GetEntryCreatedDate(int id)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            return _reader.ReadDateTime(GetEntryDataOffset(id) + OFFSET_ENTRY_CREATED_YEAR);
        }

        public void SetEntryCreatedDate(int id, DateTime dateTime)
        {
            Contract.Assert(id >= 0 && id <= Capacity);

            _writer.Write(dateTime, (int)GetEntryDataOffset(id) + OFFSET_ENTRY_CREATED_YEAR);
        }

        #endregion // Public Methods
    }
}
